/***************************************************************************************
* Copyright (c) 2014-2022 Zihao Yu, Nanjing University
*
* NEMU is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*          http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
***************************************************************************************/

#include "local-include/reg.h"
#include <cpu/cpu.h>
#include <cpu/ifetch.h>
#include <cpu/decode.h>
#include <sdb/sdb.h>
#include <sdb/func_trace.h>

#define R(i) gpr(i)
#define Mr vaddr_read
#define Mw vaddr_write

enum {
  TYPE_I, TYPE_U, TYPE_S,
  TYPE_N, TYPE_J, TYPE_R, 
  TYPE_B,// none
};

//SEXT宏的作用：
//1. 使用结构体位域（长度由len决定），降低存储空间
//2. 将数据由int64_t,转换为uint64_t。

//原因：(1)使用64位数据类型，兼容riscv32和riscv64。
//		(2)移位：无符号数的移位是算术的，而有符号数是逻辑移位，我们需要进行算术移位的时候使用。
//		(3)运算：无符号数和有符号数在计算方面，是基本一样的，但比较时一般用有符号的
//		(4)范围：无符号数的存储范围>有符号数的存储范围。

//B和J格式的分支和跳转地址必须向左移动 1 位以将地址乘以 2，从而给予分支和跳转指令更大的跳转范围
#define src1R() do { *src1 = R(rs1); } while (0)
#define src2R() do { *src2 = R(rs2); } while (0)
#define immI() do { *imm = SEXT(BITS(i, 31, 20), 12); } while(0)
#define immU() do { *imm = SEXT(BITS(i, 31, 12), 20) << 12; } while(0)
#define immS() do { *imm = (SEXT(BITS(i, 31, 25), 7) << 5) | BITS(i, 11, 7); } while(0)
#define immJ() do { *imm = SEXT(( \
						(BITS(i, 31, 31) << 19) | \
						 BITS(i, 30, 21)		| \
						(BITS(i, 20, 20) << 10) | \
						(BITS(i, 19, 12) << 11)) << 1, 21); } while(0)
#define immB() do { *imm = SEXT(( \
						(BITS(i, 31, 31) << 11) | \
						(BITS(i, 30, 25) << 4)  | \
						(BITS(i, 11, 8))		| \
						(BITS(i, 7, 7)   << 10)) << 1, 13); } while(0)

static void decode_operand(Decode *s, int *rd, word_t *src1, word_t *src2, word_t *imm, int type) {
  uint32_t i = s->isa.inst.val;
  //riscv概念的体现：rs1、rs2和rd如果有，那么它们在指令中的位置都是固定的
  int rs1 = BITS(i, 19, 15);
  int rs2 = BITS(i, 24, 20);
  *rd     = BITS(i, 11, 7);
  switch (type) { 
    case TYPE_I: src1R();          immI(); break;
    case TYPE_U:                   immU(); break;
    case TYPE_S: src1R(); src2R(); immS(); break;
	case TYPE_J:				   immJ(); break;
	case TYPE_R: src1R(); src2R();		   break;
	case TYPE_B: src1R(); src2R(); immB(); break;
  }
}

static int decode_exec(Decode *s) {
  //printf("INNEMU=%x\n",s->pc);
  int rd = 0;
  word_t src1 = 0, src2 = 0, imm = 0;//默认是无符号的，记住符号拓展时先变数据类型！！
  s->dnpc = s->snpc;

#define INSTPAT_INST(s) ((s)->isa.inst.val)
#define INSTPAT_MATCH(s, name , type, ... /* execute body */ ) { \
  decode_operand(s, &rd, &src1, &src2, &imm, concat(TYPE_, type)); \
  __VA_ARGS__ ; \
}
  INSTPAT_START();
  //printf("pc=%x\n",s->pc);
  //if(s->pc==0x8000018c) nemu_state.state=NEMU_STOP;
  //高位求值的实现参考:https://cloud.tencent.com/developer/ask/sof/140338
  //但是32bit架构也能使用int64，单独求高位会比较复杂，直接用int64求值再移位即可


  //基本运算
  INSTPAT("0000000 ????? ????? 000 ????? 01100 11", add    , R, R(rd) = src1 + src2);
  INSTPAT("0100000 ????? ????? 000 ????? 01100 11", sub    , R, R(rd) = src1 - src2);//igore arithmetic overflow
  INSTPAT("0000001 ????? ????? 000 ????? 01100 11", mul    , R, R(rd) = src1 * src2;);//得到乘积的低32bits
  INSTPAT("0000001 ????? ????? 001 ????? 01100 11", mulh   , R, R(rd) = (int64_t)(int32_t)src1 * (int64_t)(int32_t)src2 >> 32 /*printf("LOG:%lx\n",(int32_t)src1)*/);//可以得到有符号数乘积的高32bits
  INSTPAT("0000001 ????? ????? 011 ????? 01100 11", mulhu  , R, R(rd) = (uint64_t)((uint64_t)src1 * (uint64_t)src2) >> 32); 
  INSTPAT("0000001 ????? ????? 100 ????? 01100 11", div    , R, R(rd) = (int32_t)src1 / (int32_t)src2);//debug:div是有符号的
  INSTPAT("0000001 ????? ????? 110 ????? 01100 11", rem    , R, R(rd) = (int32_t)src1 % (int32_t)src2);//debug:rem是有符号的
  INSTPAT("??????? ????? ????? 000 ????? 00100 11", addi   , I, R(rd) = src1 + imm);
  INSTPAT("0000001 ????? ????? 111 ????? 01100 11", remu   , R, R(rd) = src1 % src2);
  INSTPAT("0000001 ????? ????? 101 ????? 01100 11", divu   , R, R(rd) = src1 / src2);


  //逻辑运算
  INSTPAT("0000000 ????? ????? 111 ????? 01100 11", and    , R, R(rd) = src1 & src2);
  INSTPAT("0000000 ????? ????? 100 ????? 01100 11", xor    , R, R(rd) = src1 ^ src2);
  INSTPAT("0000000 ????? ????? 110 ????? 01100 11", or     , R, R(rd) = src1 | src2);
  INSTPAT("??????? ????? ????? 111 ????? 00100 11", andi   , I, R(rd) = src1 & imm);
  INSTPAT("??????? ????? ????? 110 ????? 00100 11", ori    , I, R(rd) = src1 | imm);  
  INSTPAT("??????? ????? ????? 100 ????? 00100 11", xori   , I, R(rd) = src1 ^ imm);


  //移位运算
  INSTPAT("0000000 ????? ????? 001 ????? 01100 11", sll    , R, R(rd) = (src1 << src2));//src2的低五位为移动位数，高位忽略
  INSTPAT("0100000 ????? ????? 101 ????? 01100 11", sra    , R, R(rd) = ((int32_t)src1 >> src2));
  INSTPAT("0000000 ????? ????? 101 ????? 01100 11", srl    , R, R(rd) = (src1 >> src2));
  INSTPAT("0000000 ????? ????? 001 ????? 00100 11", slli   , I, R(rd) = src1 << BITS(imm, 5, 0));
  INSTPAT("0100000 ????? ????? 101 ????? 00100 11", srai   , I, R(rd) = ((int32_t)src1 >> imm));//riscv32下，shamt[5]==0时才有效。这里需要使用有符号数，实现算术移位
  INSTPAT("0000000 ????? ????? 101 ????? 00100 11", srli   , I, R(rd) = src1 >> BITS(imm, 5, 0)); 



  //条件置位
  INSTPAT("0000000 ????? ????? 010 ????? 01100 11", slt    , R, R(rd) = ((int32_t)src1 < (int32_t)src2));
  INSTPAT("0000000 ????? ????? 011 ????? 01100 11", sltu   , R, R(rd) = (src1 < src2));//不用三目运算
  INSTPAT("??????? ????? ????? 011 ????? 00100 11", sltiu  , I, R(rd) = (src1 < (uint32_t)imm));


  //分支语句
  INSTPAT("??????? ????? ????? 000 ????? 11000 11", beq    , B, if(src1 == src2) s->dnpc = s->pc + imm);
  INSTPAT("??????? ????? ????? 001 ????? 11000 11", bne    , B, if(src1 != src2) s->dnpc = s->pc + imm);
  INSTPAT("??????? ????? ????? 101 ????? 11000 11", bge    , B, if((int32_t)src1 >= (int32_t)src2) s->dnpc = s->pc + imm);
  INSTPAT("??????? ????? ????? 100 ????? 11000 11", blt    , B, if((int32_t)src1 < (int32_t)src2) s->dnpc = s->pc + imm); 
  INSTPAT("??????? ????? ????? 111 ????? 11000 11", bgeu   , B, if((uint32_t)src1 >= (uint32_t)src2) s->dnpc = s->pc + imm);
  INSTPAT("??????? ????? ????? 110 ????? 11000 11", bltu   , B, if((uint32_t)src1 < (uint32_t)src2) s->dnpc  = s->pc + imm );


  //跳转语句
  INSTPAT("??????? ????? ????? ??? ????? 11011 11", jal    , J, 
	#ifdef CONFIG_FTRACE_COND
	call(s->pc,s->dnpc,0);
	#endif
	R(rd) = s->pc + 4;s->dnpc = s->pc+imm);

  INSTPAT("??????? ????? ????? 000 ????? 11001 11", jalr   , I, 
    #ifdef CONFIG_FTRACE_COND
    call(s->pc,s->dnpc,(s->isa.inst.val==0x00008067));
	#endif
  R(rd) = s->pc + 4;s->dnpc = (src1 + imm) & ~(uint32_t)1);//其实这里应该用word_t，但是由于我全用的uint32_t，所以这边就继续沿用了，以后可以回来改


  //加载
  INSTPAT("??????? ????? ????? 000 ????? 00000 11", lb     , I, R(rd) = SEXT(Mr(src1 + imm, 1), 8));
  INSTPAT("??????? ????? ????? 001 ????? 00000 11", lh     , I, R(rd) = SEXT(Mr(src1 + imm, 2), 16));
  INSTPAT("??????? ????? ????? 010 ????? 00000 11", lw     , I, R(rd) = SEXT(Mr(src1 + imm, 4) ,32));//由于是riscv32，不用符号拓展
  INSTPAT("??????? ????? ????? ??? ????? 01101 11", lui    , U, R(rd) = imm);
  INSTPAT("??????? ????? ????? 100 ????? 00000 11", lbu    , I, R(rd) = Mr(src1 + imm, 1));
  INSTPAT("??????? ????? ????? 101 ????? 00000 11", lhu    , I, R(rd) = Mr(src1 + imm, 2));
  INSTPAT("??????? ????? ????? 110 ????? 00000 11", lwu    , I, R(rd) = Mr(src1 + imm, 4));  


  //存储
  INSTPAT("??????? ????? ????? 000 ????? 01000 11", sb     , S, Mw(src1 + imm, 1, src2));
  INSTPAT("??????? ????? ????? 001 ????? 01000 11", sh     , S, Mw(src1 + imm, 2, src2));
  INSTPAT("??????? ????? ????? 010 ????? 01000 11", sw     , S, Mw(src1 + imm, 4, src2));
  
  //CTE相关指令
  INSTPAT("??????? ????? ????? 011 ????? 11100 11", csrrc  , I, ncsr_t t = *csr(imm); *csr(imm) = t & ~src1; R(rd) = t);
  INSTPAT("??????? ????? ????? 010 ????? 11100 11", csrrs  , I, ncsr_t t = *csr(imm); *csr(imm) = t | src1; R(rd) = t);
  INSTPAT("??????? ????? ????? 001 ????? 11100 11", csrrw  , I, ncsr_t t = *csr(imm); *csr(imm) = src1; R(rd) = t);


  //TODO: 这里是传入s->pc还是s->snpc?
  INSTPAT("0000000 00000 00000 000 00000 11100 11",	ecall  , I, s->dnpc = isa_raise_intr(0xb,s->pc);cpu.mstatus = 0x1800);//R(17) 为 $a7);
  INSTPAT("0011000 00010 00000 000 00000 11100 11", mret   , R, s->dnpc = cpu.mepc;cpu.mstatus = 0x80);

  //其他
  INSTPAT("??????? ????? ????? ??? ????? 00101 11", auipc  , U, R(rd) = s->pc + imm;);
  INSTPAT("0000000 00001 00000 000 00000 11100 11", ebreak , N, NEMUTRAP(s->pc, R(10))); // R(10) is $a0
  INSTPAT("??????? ????? ????? ??? ????? ????? ??", inv    , N, INV(s->pc));
  
  INSTPAT_END();

  R(0) = 0; // reset $zero to 0

  return 0;
}

int isa_exec_once(Decode *s) {
  s->isa.inst.val = inst_fetch(&s->snpc, 4);
  return decode_exec(s);
}
